﻿//================================================================================
// Filename: PhotoAlbumPlugin.as
// Authour: Jacky Fong
// Description:
// Photo album
//
// Input Events
// - PhotoAlbumPlugin_SetPage
// - PhotoAlbumPlugin_LoadAlbums
//
// Output Events
// - PhotoAlbumPlugin_LoadAlbumsComplete
//================================================================================

package
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.MovieClip;
	import flash.display.Shape;
    import flash.display.Loader;
	import flash.display.Stage;
    import flash.filters.BitmapFilter;
    import flash.filters.BitmapFilterQuality;
    import flash.filters.DropShadowFilter;	
	import flash.geom.Point;
    import flash.net.URLRequest;
	import flash.net.URLLoader;
	import flash.display.Sprite;
	import flash.events.*;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	import IPlugin;
	import PluginManager;
	import PluginEvent;
	
	public class PhotoAlbumPlugin extends Sprite implements IPlugin
	{
		private static const _pluginName:String = "PhotoAlbumPlugin";
		private var _manager:PluginManager;
		private var _container:Sprite;
		private var _albumContainer:Object;
		private var _loader:Loader; // loader as container
		private var _albumList:Object;
		private var _bg:Shape;
		private var _uiLib:Object;
		private var _curPage:uint = 0;
		private var _curAlbum:String = "";
		
		// properties
		private var _startX:int = 0;
		private var _startY:int = 0;
		private var _albumSize:uint = 0;
		private var _albumSpace:uint = 0;
		private var _frameSize:uint = 0;
		private var _maxCol:uint = 0;
		private var _maxPageImage:uint = 0;
		private var _thumbnailSize:uint = 0;
		
		public function PhotoAlbumPlugin()
		{
			init();
		}
		
		public function get pluginName():String
		{
			return _pluginName;
		}
					
		public function init():void
		{
			trace( "Init " + _pluginName );
			_container = new Sprite();
			_albumContainer = new Object();
			_albumList = new Object();
		}
		
		public function setManager( manager:PluginManager ):void
		{
			_manager = manager;
			_createAlbumBG();
			_loadUI( "ui/PhotoAlbum.swf" );
			_setupEventListeners();
		}		
		
		// listen to plugin's self operations
		private function _setupEventListeners():void
		{
			if( _manager == null ) return;
			
			_manager.addEventListener( "PhotoAlbumPlugin_SetPage", _onSetPage );
			_manager.addEventListener( "PhotoAlbumPlugin_LoadAlbums", _onLoadAlbums );
		}
		
		//=====================================================================
		private function _loadUI( url:String ):void
		{
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener( Event.COMPLETE, _loadUIComplete );			
			loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, _loadUIIoError );
            var req:URLRequest = new URLRequest( url );
            loader.load( req );
		}
		
		private function _loadUIComplete( evt:Event ):void
		{
			_manager.apiLog( this + " LoadUIComplete: " + evt );
			_uiLib = evt.target.content;
		}

		private function _loadUIIoError( evt:IOErrorEvent ):void
		{
			_manager.apiLog( this + " LoadUIIoError: " + evt );
		}
		
		//==========================================================================
		// all albums
		//==========================================================================
		private function _loadAlbumsXMLComplete( evt:Event ):void
		{
			_manager.apiLog( this + " Load Albums XML Complete: " + evt );
			
			var xml:XML = new XML( evt.target.data );
			for each( var album:XML in xml..album ) {
				_albumList[ album.url ] = { isLoaded:false };
			}
			_startX = xml.properties.startX;
			_startY = xml.properties.startY;
			_albumSize = xml.properties.albumSize;
			_albumSpace = xml.properties.albumSpace;
			_frameSize = xml.properties.frameSize;
			_maxCol = xml.properties.maxCol;
			_maxPageImage = xml.properties.maxPageImage;
			_thumbnailSize = xml.properties.thumbnailSize;
	
			// start loading until all urls enqueued
			for( var a in _albumList ) {
				_loadAlbumXML( a );
			}
		}

		private function _loadAlbumsXMLIoError( evt:IOErrorEvent ):void
		{
			_manager.apiLog( this + " Load Albums XML IoError: " + evt );
		}
		
		// single album
		private function _loadAlbumXML( url:String ):void
		{
			_manager.apiLog( this + " Load Album XML: " + url );
			
			var loader:URLLoader = new URLLoader();
			var req:URLRequest = new URLRequest( url );
			loader.load( req );
			
			var func:Object = new Object();
			func.loadAlbumXMLComplete = function( evt:Event )
			{
				_manager.apiLog( this + " Load Album XML Complete: " + evt );
				_setAlbumXML( url, evt );
			}
			
			func.loadAlbumXMLIoError = function( evt:IOErrorEvent )
			{
				_manager.apiLog( this + " Load Album XML IoError: " + evt );
			}
			
			loader.addEventListener( Event.COMPLETE, func.loadAlbumXMLComplete );
			loader.addEventListener( IOErrorEvent.IO_ERROR, func.loadAlbumXMLIoError );		
		}
		
		private function _setAlbumXML( url:String, evt:Event ):void
		{
			var xml:XML = new XML( evt.target.data );
			var firstImage:String;
			var nImage:uint = 0;
			var imgList:Array = new Array();
			for each( var image:XML in xml..image ) {
				if( nImage == 0 ) { firstImage = image.attribute( "name" ) + "_t.jpg"; } //default jpg
				imgList.push( { name:image.attribute( "name" ), width:image.attribute( "width" ), height:image.attribute( "height" ),
								title:image.attribute( "title" ), description:image.attribute( "description" ) } );
				nImage++;
			}
			_albumList[ url ].imgList = imgList;
			if( xml.cover == undefined ) {
				_albumList[ url ].cover = firstImage;
			}
			else {
				_albumList[ url ].cover = xml.cover;
			}
			_albumList[ url ].title = xml.attribute( "maintitle" );
			_albumList[ url ].isLoaded = true;
			
			// check if all albums' xml completely loaded
			var isLoaded:Boolean = false;
			for( var a in _albumList ) {
				isLoaded = _albumList[ a ].isLoaded;
				if( !isLoaded ) break;
			}
			if( isLoaded ) {
				_manager.dispatchEvent( new PluginEvent( "PhotoAlbumPlugin_LoadAlbumsComplete", { container:_container } ) );
				_drawAlbumCovers();
			}
		}
			
		// draw albums' thumbnails to the container
		private function _drawAlbumCovers():void
		{
			var nImage:uint = 0;
			var totalSize = _albumSize + _albumSpace;
			// dropshadow filter			
			var filter:BitmapFilter = _createDropShadowFilter( 8, 45, 0x000000, 0.7, 5, 5, 0.65, BitmapFilterQuality.MEDIUM, false, false );
			var filters:Array = new Array();
			filters.push( filter );
					
			for( var a in _albumList ) {
				// load cover image, place it corretly
				var loader:Loader = new Loader();
				loader.contentLoaderInfo.addEventListener( Event.COMPLETE, _loadCoverImageComplete );
				loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, _loadCoverImageIoError );
				var url:String = a.substring( 0, a.lastIndexOf( "/" ) + 1 ) + _albumList[ a ].cover;
				var req:URLRequest = new URLRequest( url );
				loader.load( req );
				// frame x, y
				var frameX = _startX + int( nImage % _maxCol ) * totalSize;
				var frameY = _startY + int( nImage / _maxCol ) * totalSize;
				
				// frame
				var s:Shape = new Shape();
				s.graphics.beginFill( 0xffffff );
				//s.graphics.lineStyle( evt.args.borderSize, evt.args.borderColour, evt.args.borderAlpha );
				s.graphics.drawRect( frameX, frameY, _albumSize, _albumSize );
				s.graphics.endFill();
				s.filters = filters;
				_container.addChild( s );
				
				// photo added after frame
				loader.x = frameX + _frameSize;
				loader.y = frameY + _frameSize;
				_container.addChild( loader );
				
				// mask
				var mask:Shape = new Shape();
				mask.graphics.beginFill( 0xffffff );
				var size:Number = _albumSize - 2 * _frameSize;
				mask.graphics.drawRect( loader.x, loader.y, size, size );
				mask.graphics.endFill();
				_container.addChild( mask );
				loader.mask = mask;
				
				// title
				var title:TextField = new TextField();
				title.autoSize = TextFieldAutoSize.CENTER;
				//title.background = true;
				//title.border = true;
				
				var format:TextFormat = new TextFormat();
				format.font = "Arial Unicode MS";
				format.color = 0x663300;
				format.size = 14;
				format.bold = true;
				//format.underline = true;				
				title.defaultTextFormat = format;
				
				title.text = _albumList[ a ].title;
				title.x = loader.x;
				title.y = frameY + _albumSize + 3;
				title.width = _albumSize - 2 * _frameSize;
				_container.addChild( title );
				
				// set link
				_setAlbumLink( loader, a );
				// album container
				_albumContainer[ a ] = new Sprite();
				
				nImage++;
			}
		}
		
		//=====================================================================
		// create only, not add to container
		private function _createAlbumBG():void
		{
			var stage:Stage = _manager.apiGetStage();
			_bg = new Shape();
			_bg.graphics.beginFill( 0x000000, 0.8 );
			_bg.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
			_bg.graphics.endFill();
			_bg.visible = false;
		}
		
		//=====================================================================
		private function _setAlbumLink( loader:Loader, url:String ):void
		{
			var func:Object = new Object();
			func.onMouseClick = function( evt:Event )
			{
				if( _curAlbum != "" ) return;				
				_bg.visible = true;
				_manager.dispatchEvent( new PluginEvent( "NavigationBarPlugin_Show", { show:false } ) );
				_showAlbum( url );
			}							
			loader.addEventListener( MouseEvent.CLICK, func.onMouseClick );
		}

		private function _createDropShadowFilter( distance:Number, angle:Number, color:uint, alpha:Number,
												  blurX:Number, blurY:Number, strength:Number, quality:int,
												  inner:Boolean, knockout:Boolean ):BitmapFilter
		{
			return new DropShadowFilter(distance, angle, color,	alpha,
										blurX, blurY, strength, quality,
										inner, knockout );
		}
		
		private function _loadCoverImageComplete( evt:Event ):void
		{
			_manager.apiLog( this + " LoadCoverImageComplete: " + evt );
			
			// adjust cover size
			var size:Number = _albumSize - 2 * _frameSize;
			if( evt.target.width < evt.target.height ) {
				var ratioW:Number = evt.target.height / evt.target.width;
				evt.target.loader.width = size;
				evt.target.loader.height = ratioW * evt.target.loader.width;
			}
			else {
				var ratioH:Number = evt.target.width / evt.target.height;
				evt.target.loader.height = size;
				evt.target.loader.width = ratioH * evt.target.loader.height;				
			}
		}

		private function _loadCoverImageIoError( evt:IOErrorEvent ):void
		{
			_manager.apiLog( this + " LoadCoverImageIoError: " + evt );
		}
		
		//==========================================================================
		// album contents
		//==========================================================================
		private function _showAlbum( url:String, page:uint = 0 ):void
		{
			_curAlbum = url;
			
			// remove contents of all albums
			for( var a in _albumContainer ) {
				_removeDisplayObjectContainerChildren( _albumContainer[ a ] );
			}			
			_container.addChild( _bg );
			_container.addChild( _albumContainer[ url ] );
			
			var stage:Stage = _manager.apiGetStage();
			var dir:String = url.substring( 0, url.lastIndexOf( "/" ) + 1 );
			var nImage:uint = _albumList[ url ].imgList.length;
			var nPage:uint = nImage / _maxPageImage;
			if( nImage % _maxPageImage > 0 ) { nPage++;	}
			_curPage = Math.max( 0, page );
			_curPage = Math.min( _curPage, nPage - 1 );
			var startImg:uint = _curPage * _maxPageImage;			
			var endImg:uint = startImg + _maxPageImage; // last image + 1
			if( endImg > nImage ) { endImg = nImage; }

			var cImage:uint = 0;			
			var startX:uint = ( stage.stageWidth - ( _maxPageImage * _thumbnailSize ) ) * 0.5;
			
			// thumbnails
			for( var i:uint=startImg; i<endImg; i++ ) {
				var img:Object = _albumList[ url ].imgList[ i ];				
				var tnUrl:String = dir + img.name + "_t.jpg";				
				//_manager.apiLog( "img: " + img.name + " " + img.width + " " + img.height + " " + img.title );
				
				// load thumbnails
				var loader:Loader = new Loader();
				loader.contentLoaderInfo.addEventListener( Event.COMPLETE, _loadThumbnailComplete );
				loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, _loadThumbnailIoError );
				var req:URLRequest = new URLRequest( tnUrl );
				loader.load( req );
				
				loader.alpha = 0.5;
				loader.x = startX + cImage * _thumbnailSize;
				loader.y = stage.stageHeight - _thumbnailSize - 35;
				_albumContainer[ url ].addChild( loader );
				
				// set link
				_setThumbnailLink( loader, url, i );
				// todo: on mouse over, thumbnail fx
				
				cImage++;
			}

			_setPageNumber( nPage );
			_setUI( nPage );
			
			// first image
			_showImage( url, startImg );
		}
		
		private function _setPageNumber( nPage:uint ):void
		{
			var stage:Stage = _manager.apiGetStage();
			var pageNumber:TextField = new TextField();
			pageNumber.autoSize = TextFieldAutoSize.CENTER;
			
			var format:TextFormat = new TextFormat();
			format.font = "Myriad Pro";
			format.color = 0xffffff;
			format.size = 12;
			pageNumber.defaultTextFormat = format;
			
			pageNumber.text = "page " + String( _curPage + 1 ) + "/" + nPage;
			pageNumber.width = 100;
			pageNumber.x = ( stage.stageWidth - pageNumber.width ) * 0.5;
			pageNumber.y = stage.stageHeight - 35;			
			_albumContainer[ _curAlbum ].addChild( pageNumber );			
		}
		
		private function _setUI( nPage:uint ):void
		{
			var stage:Stage = _manager.apiGetStage();
			var stageMid:uint = stage.stageWidth * 0.5;
			
			if( _curPage < nPage - 1 ) {
				var arrowRight:MovieClip = new _uiLib.PhotoAlbumArrowRight();
				arrowRight.x = stageMid + 60;
				arrowRight.y = stage.stageHeight - 25;
				_albumContainer[ _curAlbum ].addChild( arrowRight );
				arrowRight.addEventListener( MouseEvent.CLICK, _arrowRightOnMouseClick );
			}
			
			if( _curPage > 0 ) {
				var arrowLeft:MovieClip = new _uiLib.PhotoAlbumArrowLeft();
				arrowLeft.x = stageMid - 60;
				arrowLeft.y = stage.stageHeight - 25;
				_albumContainer[ _curAlbum ].addChild( arrowLeft );			
				arrowLeft.addEventListener( MouseEvent.CLICK, _arrowLeftOnMouseClick );
			}
			
			var close:MovieClip = new _uiLib.PhotoAlbumClose();
			close.x = 30;
			close.y = stage.stageHeight - 25;
			_albumContainer[ _curAlbum ].addChild( close );
			close.addEventListener( MouseEvent.CLICK, _closeOnMouseClick );
			
			var loading:MovieClip = new _uiLib.PhotoAlbumLoading();
			loading.x = stage.stageWidth * 0.5;
			loading.y = ( stage.stageHeight - _thumbnailSize - 25 ) * 0.5;
			_albumContainer[ _curAlbum ].addChild( loading );
		}
		
		// ui events
		private function _arrowRightOnMouseClick( evt:MouseEvent ):void
		{
			_showAlbum( _curAlbum, _curPage + 1 );
		}

		private function _arrowLeftOnMouseClick( evt:MouseEvent ):void
		{
			_showAlbum( _curAlbum, _curPage - 1 );
		}
		
		private function _closeOnMouseClick( evt:MouseEvent ):void
		{
			_clearAlbums();
			_manager.dispatchEvent( new PluginEvent( "NavigationBarPlugin_Show", { show:true } ) );
		}
		
		private function _clearAlbums():void
		{			
			_container.removeChild( _bg );
			_container.removeChild( _albumContainer[ _curAlbum ] );
			_curAlbum = "";
		}
		
		private function _loadThumbnailComplete( evt:Event ):void
		{
			_manager.apiLog( this + " LoadThumbnailComplete: " + evt );
			// centre thumbnail
			var wDiff:Number = _thumbnailSize - evt.target.width;
			var hDiff:Number = _thumbnailSize - evt.target.height;
			if( wDiff > 0 ) {
				evt.target.loader.x += ( wDiff ) * 0.5;
			}
			if( hDiff > 0 ) {
				evt.target.loader.y += ( hDiff ) * 0.5;
			}			
		}

		private function _loadThumbnailIoError( evt:IOErrorEvent ):void
		{
			_manager.apiLog( this + " LoadThumbnailIoError: " + evt );
		}		
		
		private function _setThumbnailLink( loader:Loader, url:String, index:uint ):void
		{
			var func:Object = new Object();
			func.onMouseClick = function( evt:MouseEvent )
			{
				_showImage( url, index );
			}
			func.onMouseOver = function( evt:MouseEvent )
			{
				loader.alpha = 1.0;
			}
			func.onMouseOut = function( evt:MouseEvent )
			{
				loader.alpha = 0.5;
			}			
			loader.addEventListener( MouseEvent.CLICK, func.onMouseClick );
			loader.addEventListener( MouseEvent.MOUSE_OVER, func.onMouseOver );
			loader.addEventListener( MouseEvent.MOUSE_OUT, func.onMouseOut );
		}
		
		// show image at index in _albumList[ url ]
		private function _showImage( url:String, index:uint ):void
		{
			// remove previous contents
			var imgCon:DisplayObject = _albumContainer[ url ].getChildByName( "imageContainer" );
			if( imgCon != null ) {
				_albumContainer[ url ].removeChild( imgCon );
			}
			var imgFrame:DisplayObject = _albumContainer[ url ].getChildByName( "imageFrame" );
			if( imgFrame != null ) {
				_albumContainer[ url ].removeChild( imgFrame );
			}			
			
			var dir:String = url.substring( 0, url.lastIndexOf( "/" ) + 1 );
			var imgUrl:String = dir + _albumList[ url ].imgList[ index ].name + ".jpg";
			
			var loader:Loader = new Loader();
			loader.name = "imageContainer";
			var func:Object = new Object();
			func.loadImageComplete = function( evt:Event )
			{
				_manager.apiLog( this + " LoadImageComplete: " + evt );
				
				var stage:Stage = _manager.apiGetStage();			
				loader.x = ( stage.stageWidth - evt.target.width ) * 0.5;
				loader.y = ( stage.stageHeight - _thumbnailSize - 25 - evt.target.height ) * 0.5;
				
				// frame
				var frameX = loader.x - _frameSize;
				var frameY = loader.y - _frameSize;
				var frameSize2:uint = 2 * _frameSize;
								
				var imgFrame:DisplayObject = _albumContainer[ url ].getChildByName( "imageFrame" );
				// create
				if( imgFrame == null ) {
					var s:Shape = new Shape();
					s.name = "imageFrame";
					s.graphics.beginFill( 0xffffff );
					s.graphics.drawRect( 0, 0, evt.target.width + frameSize2, evt.target.height + frameSize2 );
					s.graphics.endFill();
					s.x = frameX;
					s.y = frameY;
					_albumContainer[ url ].addChild( s );					
				}
				// resize
				// todo: animate frame size changing
				else {
					imgFrame.x = frameX;
					imgFrame.y = frameY;
					imgFrame.width = evt.target.width + frameSize2;
					imgFrame.height = evt.target.height + frameSize2;
				}
				_albumContainer[ url ].addChild( loader );
			}
			
			func.loadImageIoError = function( evt:IOErrorEvent )
			{
				_manager.apiLog( this + " LoadImageIoError: " + evt );
			}
			
			loader.contentLoaderInfo.addEventListener( Event.COMPLETE, func.loadImageComplete );
			loader.contentLoaderInfo.addEventListener( IOErrorEvent.IO_ERROR, func.loadImageIoError );			
			var req:URLRequest = new URLRequest( imgUrl );
			loader.load( req );		
		}

		//==========================================================================
		// helpers
		//==========================================================================
		// remove all children from a DisplayObjectContainer
		private function _removeDisplayObjectContainerChildren( container:DisplayObjectContainer ):void
		{
			var children:Array = new Array();
			for( var i:uint=0; i<container.numChildren; i++ ) {
				children.push( container.getChildAt( i ) );
			}
			for( i=0; i<children.length; i++ ) {
				container.removeChild( children[i] );
			}			
		}
		
		//==========================================================================
		// plugin API
		//==========================================================================
		// specially for page plugin, set the album to a specific page
		private function _onSetPage( evt:PluginEvent ):void
		{
			_manager.apiLog( this + " _onSetPage: " + evt );
			
			_manager.dispatchEvent( new PluginEvent( "PagePlugin_AddPageDisplayObjectContainer", { page:evt.args.page, container:_container } ) );
		}
		
		private function _onLoadAlbums( evt:PluginEvent ):void
		{
			_manager.apiLog( this + " _onLoadAlbums: " + evt );
						
			var loader:URLLoader = new URLLoader();
			var req:URLRequest = new URLRequest( evt.args.url );
			loader.load( req );
			loader.addEventListener( Event.COMPLETE, _loadAlbumsXMLComplete );
			loader.addEventListener( IOErrorEvent.IO_ERROR, _loadAlbumsXMLIoError );
		}	
	}
}